#include "PageCache.h"


PageCache PageCache::_sinst;


Span* PageCache::NewSpan(size_t k) {
	
	if (k > NPAGES - 1) {
		void* ptr = SystemAlloc(k);
		Span* span = objectSpan.New();
		//Span* span = new Span();
		assert(span);
		span->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
		span->_n = k;
		//span->_isUsed = true;
		//_idSpanMap[span->_pageId] = span;
		//_pagemap.set(span->_pageId, span); 
		PageCache::GetInstance()->MapObjectSetSpan(span->_pageId, span);
		return span;
	}



	//Ѱ��kҳ��ϣͰ��span
	SpanList* list = &_spanLists[k];
	if (!list->Empty()) {
		//���span��Ϊ��ֱ�ӷ���span
		Span* kSpan =  list->FrontPop();
		//��span��ҳidӳ����unordered_map��ȥ
		for (size_t i = 0; i < kSpan->_n; ++i) {
			//_idSpanMap[kSpan->_pageId + i] = kSpan;
			//_pagemap.set(kSpan->_pageId + i, kSpan);
			PageCache::GetInstance()->MapObjectSetSpan(kSpan->_pageId + i, kSpan);
		}
		return kSpan;
	}

	//Ϊ������ҽ�����kҳ�����span
	for (size_t i = k + 1; i < NPAGES; ++i) {
		if (!_spanLists[i].Empty()) {
			SpanList* list = &_spanLists[i];

			//��nҳ��span�з�Ϊkҳ��span �� n - k ҳ��span;
			Span* span = list->FrontPop();
			Span* kSpan = objectSpan.New();	
			//Span* kSpan = new Span();
			assert(kSpan);
			kSpan->_pageId = span->_pageId;
			kSpan->_n = k;
			span->_pageId += k;
			span->_n -= k;


			//��n - k ��span����spanlist��
			_spanLists[span->_n].FrontPush(span);
			//��n - k ҳ��span����β��ַӳ���unorderes_map��ȥ
			//_idSpanMap[span->_pageId] = span;
			//_pagemap.set(span->_pageId, span);
			PageCache::GetInstance()->MapObjectSetSpan(span->_pageId, span);
			//_idSpanMap[span->_pageId + span->_n - 1] = span;
			//_pagemap.set(span->_pageId + span->_n - 1, span);
			PageCache::GetInstance()->MapObjectSetSpan(span->_pageId + span->_n - 1, span);




			//��span��ҳidӳ����unordered_map��ȥ
			for (size_t i = 0; i < kSpan->_n; ++i) {

				//_idSpanMap[kSpan->_pageId + i] = kSpan;
				//_pagemap.set(kSpan->_pageId + i, kSpan);
				PageCache::GetInstance()->MapObjectSetSpan(kSpan->_pageId + i, kSpan);
			}

			//��kҳ��span����
			return kSpan;
		}
	}
	//������һ��span��û����ֱ����ϵͳ�������ҳ���ڴ�
	Span* maxSpan = objectSpan.New();
	//Span* maxSpan = new Span();
	maxSpan->_n = NPAGES - 1;
	void* ptr = SystemAlloc(NPAGES - 1);
	maxSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
	_spanLists[maxSpan->_n].FrontPush(maxSpan);
	return NewSpan(k);
}
void* PageCache::MapObjectToSpan(PAGE_ID id) {
	//std::unique_lock<std::mutex> lock(mtx);


#if defined(_WIN64) || defined(__unix__)
	std::unique_lock<std::mutex> lock(mtx1);
	if (auto it = _idSpanMap.find(id); it != _idSpanMap.end()) {
		return it->second;
	}
	
#elif defined(_WIN32)
	if (auto* span = _pagemap.get(id)) {
		return span;
	}
	
#endif

}

void PageCache::MapObjectSetSpan(PAGE_ID id, Span* span) {
	

#if defined(_WIN64) || defined(__unix__)
	std::unique_lock<std::mutex> lock(mtx1);
	_idSpanMap[id] = span;
#elif defined(_WIN32)
	_pagemap.set(id, span);
#endif
}



void PageCache::ReleaseSpanToPageCache(Span* span) {
	if (span->_n > NPAGES - 1) {
		void* ptr = (void*)(span->_pageId << PAGE_SHIFT);
#if defined(_WIN64) || defined(_WIN32) 
		SystemFree(ptr);
#elif defined(__unix__)
		SystemFree(ptr, span->_n);
#endif
		objectSpan.Delete(span);
		//delete span;
		return;
	}

	while (true) {

		//����ҳ��ǰһҳ�ϲ�
		PAGE_ID id = span->_pageId - 1;
		//auto ret = _idSpanMap.find(id);
		/*if (ret == _idSpanMap.end()) {
			break;
		}*/

	    //Span* preSpan = _idSpanMap[id];
		//Span* preSpan = (Span*)_pagemap.get(id);
		Span* preSpan = (Span*)PageCache::GetInstance()->MapObjectToSpan(id);
		if (preSpan == nullptr) {
			break;
		}


		if (preSpan->_isUsed == true) {
			break;
		}

		if ((preSpan->_n + span->_n) > NPAGES - 1) {
			break;
		}

		span->_pageId = preSpan->_pageId;
		span->_n += preSpan->_n;


		_spanLists[preSpan->_n].Erase(preSpan);
		objectSpan.Delete(preSpan);
		//delete preSpan;
	}

	//����ҳ������ҳ�ϲ�


	while (true) {

		PAGE_ID id = span->_n + span->_pageId;
		//auto ret = _idSpanMap.find(id);
		/*if (ret == _idSpanMap.end()) {
			break;
		}*/

		//Span* nextSpan = _idSpanMap[id];
		//Span* nextSpan = (Span*)_pagemap.get(id);
		Span* nextSpan = (Span*)PageCache::GetInstance()->MapObjectToSpan(id);
		
		if (nextSpan == nullptr) {
			break;
		}


		if (nextSpan->_isUsed == true) {
			break;
		}

		if ((nextSpan->_n + span->_n) > NPAGES - 1) {
			break;
		}

		span->_n += nextSpan->_n;
		_spanLists[nextSpan->_n].Erase(nextSpan);


		objectSpan.Delete(nextSpan);
		//delete nextSpan;
	}

	span->_isUsed = false;
	_spanLists[span->_n].FrontPush(span);
	

	//_idSpanMap[span->_pageId] = span;
	//_pagemap.set(span->_pageId, span);
	PageCache::GetInstance()->MapObjectSetSpan(span->_pageId, span);
	//_idSpanMap[span->_pageId + span->_n - 1] = span;
	//_pagemap.set(span->_pageId + span->_n - 1, span);
	PageCache::GetInstance()->MapObjectSetSpan(span->_pageId + span->_n - 1, span);

	
}